package org.devio.takephoto.app;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.content.Intent;
import ohos.agp.window.dialog.CommonDialog;
import ohos.utils.PacMap;
import ohos.utils.net.Uri;
import org.devio.takephoto.ResourceTable;
import org.devio.takephoto.compress.CompressConfig;
import org.devio.takephoto.compress.CompressImage;
import org.devio.takephoto.compress.CompressImageImpl;
import org.devio.takephoto.model.*;
import org.devio.takephoto.permission.PermissionManager;
import org.devio.takephoto.uitl.*;

import java.io.File;
import java.util.ArrayList;

/**
 * Date: 2016/9/21 0007 20:10
 * Version:4.0.0
 * http://www.devio.org
 * GitHub:https://github.com/crazycodeboy
 * Email:crazycodeboy@gmail.com
 */
public class TakePhotoImpl implements TakePhoto {
    private TContextWrap contextWrap;
    private TakeResultListener listener;
    private Uri outPutUri;
    private Uri tempUri;
    private CropOptions cropOptions;
    private TakePhotoOptions takePhotoOptions;
    private CompressConfig compressConfig;
    private MultipleCrop multipleCrop;
    private PermissionManager.TPermissionType permissionType;
    private TImage.FromType fromType;
    private boolean showCompressDialog;
    private CommonDialog wailLoadDialog;

    public TakePhotoImpl(Ability ability, TakeResultListener listener) {
        contextWrap = TContextWrap.of(ability);
        this.listener = listener;
    }

    TakePhotoImpl(Fraction fraction, TakeResultListener listener) {
        contextWrap = TContextWrap.of(fraction);
        this.listener = listener;
    }

    @Override
    public void onCreate(Intent savedInstanceState) {
        if (savedInstanceState != null) {
            cropOptions = savedInstanceState.getSerializableParam(TConstant.PC_CROP_OPTIONS);
            takePhotoOptions = savedInstanceState.getSerializableParam(TConstant.PC_TAKE_PHOTO_OPTIONS);
            showCompressDialog = savedInstanceState.getBooleanParam(TConstant.PC_SHOW_COMPRESSION_DIALOG, false);
            outPutUri = savedInstanceState.getParcelableParam(TConstant.PC_OUTPUT_URI);
            tempUri = savedInstanceState.getParcelableParam(TConstant.PC_TEMP_URI);
            compressConfig = savedInstanceState.getSerializableParam(TConstant.PC_COMPRESS_CONFIG);
        }
    }

    @Override
    public void onSaveInstanceState(PacMap outState) {
        outState.putSerializableObject(TConstant.PC_CROP_OPTIONS, cropOptions);
        outState.putSerializableObject(TConstant.PC_TAKE_PHOTO_OPTIONS, takePhotoOptions);
        outState.putBooleanValue(TConstant.PC_SHOW_COMPRESSION_DIALOG, showCompressDialog);
        outState.putSequenceableObject(TConstant.PC_OUTPUT_URI, outPutUri);
        outState.putSequenceableObject(TConstant.PC_TEMP_URI, tempUri);
        outState.putSerializableObject(TConstant.PC_COMPRESS_CONFIG, compressConfig);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (null != data) {
            String uri = data.getStringParam(TConstant.REQUEST_FILE_PATH);
            if (!TextUtils.isEmpty(uri)) {
                outPutUri = Uri.getUriFromFile(new File(uri));
            }
        }
        switch (requestCode) {
            case TConstant.RC_PICK_PICTURE_FROM_CAPTURE:
                if (resultCode == TConstant.CAMERA_SUCCESS) {
                    try {
                        takeResult(TResult.of(TImage.of(TUriParse.getFilePathWithUri(outPutUri, contextWrap.getAbility()), fromType)));
                    } catch (TException e) {
                        takeResult(TResult.of(TImage.of(outPutUri, fromType)), e.getDetailMessage());
                        e.printStackTrace();
                    }
                } else {
                    listener.takeCancel();
                }
                break;
            case TConstant.RC_PICK_PICTURE_FROM_CAPTURE_CROP:
                if (resultCode == TConstant.CAMERA_SUCCESS) {
                    if (takePhotoOptions != null && takePhotoOptions.isCorrectImage()) {
                        ImageRotateUtil.of().correctImage(contextWrap.getAbility(), tempUri);
                    }
                    try {
                        onCrop(tempUri, Uri.getUriFromFile(new File(TUriParse.parseOwnUri(contextWrap.getAbility(), outPutUri))), cropOptions);
                    } catch (TException e) {
                        takeResult(TResult.of(TImage.of(outPutUri, fromType)), e.getDetailMessage());
                        e.printStackTrace();
                    }
                } else {
                    listener.takeCancel();
                }
                break;
        }
    }

    @Override
    public void onPickMultiple(int limit) {
        if (PermissionManager.TPermissionType.WAIT.equals(permissionType)) {
            return;
        }
        TUtils.startActivityForResult(contextWrap,
                new TIntentWap(IntentUtils.getPickMultipleIntent(), TConstant.RC_PICK_MULTIPLE));
    }

    @Override
    public void onPickMultipleWithCrop(int limit, CropOptions options) {
        this.fromType = TImage.FromType.OTHER;
        onPickMultiple(limit);
        this.cropOptions = options;
    }

    @Override
    public void onPickFromDocuments() {
        selectPicture(0, false);
    }

    @Override
    public void onPickFromDocumentsWithCrop(Uri outPutUri, CropOptions options) {
        this.cropOptions = options;
        this.outPutUri = outPutUri;
        selectPicture(0, true);
    }

    @Override
    public void onPickFromGallery() {
        selectPicture(1, false);
    }

    @Override
    public void onPickFromGalleryWithCrop(Uri outPutUri, CropOptions options) {
        this.cropOptions = options;
        this.outPutUri = outPutUri;
        selectPicture(1, true);
    }

    @Override
    public void onPickFromCapture(Uri outPutUri) {
        this.fromType = TImage.FromType.CAMERA;
        if (PermissionManager.TPermissionType.WAIT.equals(permissionType)) {
            return;
        }
        this.outPutUri = TUriParse.convertFileUriToFileProviderUri(contextWrap.getAbility(), outPutUri);

        try {
            TUtils.captureBySafely(contextWrap,
                    new TIntentWap(IntentUtils.getCaptureIntent(this.outPutUri), TConstant.RC_PICK_PICTURE_FROM_CAPTURE));
        } catch (TException e) {
            takeResult(TResult.of(TImage.of("", fromType)), e.getDetailMessage());
            e.printStackTrace();
        }
    }

    @Override
    public void onPickFromCaptureWithCrop(Uri outPutUri, CropOptions options) {
        this.fromType = TImage.FromType.CAMERA;
        if (PermissionManager.TPermissionType.WAIT.equals(permissionType)) {
            return;
        }
        this.cropOptions = options;
        this.outPutUri = outPutUri;
        this.tempUri = TUriParse.getTempUri(contextWrap.getAbility());

        try {
            TUtils.captureBySafely(contextWrap,
                    new TIntentWap(IntentUtils.getCaptureIntent(this.tempUri), TConstant.RC_PICK_PICTURE_FROM_CAPTURE_CROP));
        } catch (TException e) {
            takeResult(TResult.of(TImage.of("", fromType)), e.getDetailMessage());
            e.printStackTrace();
        }
    }

    private void selectPicture(int defaultIndex, boolean isCrop) {
        this.fromType = TImage.FromType.OTHER;
        if (takePhotoOptions != null && takePhotoOptions.isWithOwnGallery()) {
            onPickMultiple(1);
            return;
        }
        if (PermissionManager.TPermissionType.WAIT.equals(permissionType)) {
            return;
        }
        ArrayList<TIntentWap> intentWapList = new ArrayList<>();
        intentWapList.add(new TIntentWap(IntentUtils.getPickIntentWithDocuments(),
                isCrop ? TConstant.RC_PICK_PICTURE_FROM_DOCUMENTS_CROP : TConstant.RC_PICK_PICTURE_FROM_DOCUMENTS_ORIGINAL));
        intentWapList.add(new TIntentWap(IntentUtils.getPickIntentWithGallery(),
                isCrop ? TConstant.RC_PICK_PICTURE_FROM_GALLERY_CROP : TConstant.RC_PICK_PICTURE_FROM_GALLERY_ORIGINAL));
        try {
            TUtils.sendIntentBySafely(contextWrap, intentWapList, defaultIndex, isCrop);
        } catch (TException e) {
            takeResult(TResult.of(TImage.of("", fromType)), e.getDetailMessage());
            e.printStackTrace();
        }
    }

    @Override
    public void setTakePhotoOptions(TakePhotoOptions options) {
        this.takePhotoOptions = options;
    }

    @Override
    public void onEnableCompress(CompressConfig config, boolean showCompressDialog) {
        this.compressConfig = config;
        this.showCompressDialog = showCompressDialog;
    }

    @Override
    public void permissionNotify(PermissionManager.TPermissionType type) {
        this.permissionType = type;
    }

    @Override
    public void onCrop(Uri imageUri, Uri outPutUri, CropOptions options) throws TException {
        if (PermissionManager.TPermissionType.WAIT.equals(permissionType)) {
            return;
        }
        this.outPutUri = outPutUri;
        if (!TImageFiles.checkMimeType(contextWrap.getAbility(), TImageFiles.getMimeType(contextWrap.getAbility(), imageUri))) {
            UiUtil.showToast(contextWrap.getAbility(), ResUtil.getString(contextWrap.getAbility(), ResourceTable.String_tip_type_not_image));
            throw new TException(TExceptionType.TYPE_NOT_IMAGE);
        }
        cropWithNonException(imageUri, outPutUri, options);
    }

    private void cropWithNonException(Uri imageUri, Uri outPutUri, CropOptions options) {
        this.outPutUri = outPutUri;
        if (options.isWithOwnCrop()) {
            TUtils.cropWithOwnApp(contextWrap, imageUri, outPutUri, options);
        } else {
            TUtils.cropWithOtherAppBySafely(contextWrap, imageUri, outPutUri, options);
        }
    }

    private void takeResult(final TResult result, final String... message) {
        if (null == compressConfig) {
            handleTakeCallBack(result, message);
        } else {
            if (showCompressDialog) {
                wailLoadDialog = TUtils.showProgressDialog(contextWrap.getAbility(),
                        ResUtil.getString(contextWrap.getAbility(), ResourceTable.String_tip_compress));
            }

            CompressImageImpl.of(contextWrap.getAbility(), compressConfig, result.getImages(), new CompressImage.CompressListener() {
                @Override
                public void onCompressSuccess(ArrayList<TImage> images) {
                    if (!compressConfig.isEnableReserveRaw()) {
                        deleteRawFile(images);
                    }
                    handleTakeCallBack(result);
                    if (wailLoadDialog != null && !contextWrap.getAbility().isTerminating()) {
                        contextWrap.getAbility().getUITaskDispatcher().asyncDispatch(new Runnable() {
                            @Override
                            public void run() {
                                wailLoadDialog.hide();
                            }
                        });
                    }
                }

                @Override
                public void onCompressFailed(ArrayList<TImage> images, String msg) {
                    if (!compressConfig.isEnableReserveRaw()) {
                        deleteRawFile(images);
                    }
                    handleTakeCallBack(TResult.of(images),
                            String.format(ResUtil.getString(contextWrap.getAbility(), ResourceTable.String_tip_compress_failed),
                                    message.length > 0 ? message[0] : "", msg, result.getImage().getCompressPath()));
                    if (wailLoadDialog != null && !contextWrap.getAbility().isTerminating()) {
                        contextWrap.getAbility().getUITaskDispatcher().asyncDispatch(new Runnable() {
                            @Override
                            public void run() {
                                wailLoadDialog.hide();
                            }
                        });
                    }
                }
            }).compress();
        }
    }

    @Override
    public void onCrop(MultipleCrop multipleCrop, CropOptions options) throws TException {
        this.multipleCrop = multipleCrop;
        onCrop(multipleCrop.getUris().get(0), multipleCrop.getOutUris().get(0), options);
    }

    private void deleteRawFile(ArrayList<TImage> images) {
        for (TImage image : images) {
            if (TImage.FromType.CAMERA == fromType) {
                TFileUtils.delete(image.getOriginalPath());
                image.setOriginalPath("");
            }
        }
    }

    private void handleTakeCallBack(final TResult result, String... message) {
        if (message.length > 0) {
            listener.takeFail(result, message[0]);
        } else if (multipleCrop != null && multipleCrop.hasFailed) {
            listener.takeFail(result, ResUtil.getString(contextWrap.getAbility(), ResourceTable.String_msg_crop_failed));
        } else if (compressConfig != null) {
            boolean hasFailed = false;
            for (TImage image : result.getImages()) {
                if (image == null || !image.isCompressed()) {
                    hasFailed = true;
                    break;
                }
            }
            if (hasFailed) {
                listener.takeFail(result, ResUtil.getString(contextWrap.getAbility(), ResourceTable.String_msg_compress_failed));
            } else {
                listener.takeSuccess(result);
            }
        } else {
            listener.takeSuccess(result);
        }
        clearParams();
    }

    private void clearParams() {
        compressConfig = null;
        takePhotoOptions = null;
        cropOptions = null;
        multipleCrop = null;
    }
}
